package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面类：实现公共字段自动填充处理逻辑
 *
 * 功能：在外卖平台管理系统中，自动为员工管理和菜品分类管理中的新增/修改操作填充公共字段
 * 包括：创建时间(create_time)、创建人(create_user)、更新时间(update_time)、更新人(update_user)
 */
@Aspect       // 声明这是一个切面类，用于拦截特定方法
@Component    // 声明为Spring组件，由Spring容器管理
@Slf4j        // 使用Lombok的日志注解
public class AutoFillAspect {

    /**
     * 定义切入点：指定哪些方法需要被拦截
     *
     * 规则：
     * 1. 拦截com.sky.mapper包下的所有方法
     * 2. 只拦截带有@AutoFill注解的方法
     *
     * 应用场景：员工管理和菜品分类管理的新增/修改操作
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){
        // 方法体为空，仅用于定义切入点
    }

    /**
     * 前置通知：在目标方法执行前进行公共字段的自动填充
     *
     * @param joinPoint 连接点对象，包含被拦截方法的信息
     */
    @Before("autoFillPointCut()") // 在切入点方法执行前触发
    public void autoFill(JoinPoint joinPoint) {
        log.info("【公共字段填充】开始为员工/分类数据自动填充公共字段...");

        // 1. 获取当前被拦截方法的数据库操作类型（新增INSERT/修改UPDATE）
        // ----------------------------------------------------------
        // 获取方法签名对象（包含方法名、参数类型等信息）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        // 获取方法上的@AutoFill注解对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);

        // 从注解中获取操作类型（INSERT或UPDATE）
        OperationType operationType = autoFill.value();

        // 2. 获取被拦截方法的参数（实体对象）
        // ----------------------------------------------------------
        // 获取方法的所有参数
        Object[] args = joinPoint.getArgs();

        // 如果没有参数，直接返回（无需填充）
        if (args == null || args.length == 0) {
            log.warn("⚠️【公共字段填充】警告：未找到需要填充的实体对象！");
            return;
        }

        // 约定：第一个参数就是要填充的实体对象（如Employee或Category）
        Object entity = args[0];
        log.info("📝【公共字段填充】处理对象：{}", entity.getClass().getSimpleName());

        // 3. 准备要填充的数据
        // ----------------------------------------------------------
        LocalDateTime now = LocalDateTime.now();      // 当前时间（用于创建/更新时间）
        Long currentId = BaseContext.getCurrentId();  // 当前操作员ID（从线程上下文获取）
        log.info("⏱️【公共字段填充】填充数据：时间={}, 操作员ID={}", now, currentId);

        // 4. 根据操作类型填充不同字段
        // ----------------------------------------------------------
        if (operationType == OperationType.INSERT) {
            // 新增操作：填充4个公共字段
            log.info("➕【公共字段填充】操作类型：新增数据");
            try {
                // 4.1 通过反射获取setter方法
                // --------------------------------
                // 获取设置创建时间的方法
                Method setCreateTime = entity.getClass().getDeclaredMethod(
                        AutoFillConstant.SET_CREATE_TIME, // 方法名常量："setCreateTime"
                        LocalDateTime.class                // 参数类型
                );

                // 获取设置创建人的方法
                Method setCreateUser = entity.getClass().getDeclaredMethod(
                        AutoFillConstant.SET_CREATE_USER, // "setCreateUser"
                        Long.class
                );

                // 获取设置更新时间的方法
                Method setUpdateTime = entity.getClass().getDeclaredMethod(
                        AutoFillConstant.SET_UPDATE_TIME, // "setUpdateTime"
                        LocalDateTime.class
                );

                // 获取设置更新人的方法
                Method setUpdateUser = entity.getClass().getDeclaredMethod(
                        AutoFillConstant.SET_UPDATE_USER, // "setUpdateUser"
                        Long.class
                );

                // 4.2 通过反射调用setter方法填充数据
                // ----------------------------------
                setCreateTime.invoke(entity, now);       // 设置创建时间
                log.info("✅  已填充[创建时间]");

                setCreateUser.invoke(entity, currentId); // 设置创建人
                log.info("✅  已填充[创建人]");

                setUpdateTime.invoke(entity, now);       // 设置更新时间
                log.info("✅  已填充[更新时间]");

                setUpdateUser.invoke(entity, currentId); // 设置更新人
                log.info("✅  已填充[更新人]");

            } catch (Exception e) {
                // 捕获并记录反射操作中的异常
                log.error("❌【公共字段填充】填充失败：{}", e.getMessage());
                e.printStackTrace();
            }
        } else if (operationType == OperationType.UPDATE) {
            // 修改操作：只填充2个更新字段
            log.info("✏️【公共字段填充】操作类型：修改数据");
            try {
                // 4.1 通过反射获取setter方法
                // --------------------------------
                Method setUpdateTime = entity.getClass().getDeclaredMethod(
                        AutoFillConstant.SET_UPDATE_TIME, // "setUpdateTime"
                        LocalDateTime.class
                );

                Method setUpdateUser = entity.getClass().getDeclaredMethod(
                        AutoFillConstant.SET_UPDATE_USER, // "setUpdateUser"
                        Long.class
                );

                // 4.2 通过反射调用setter方法填充数据
                // ----------------------------------
                setUpdateTime.invoke(entity, now);       // 设置更新时间
                log.info("✅  已更新[更新时间]");

                setUpdateUser.invoke(entity, currentId); // 设置更新人
                log.info("✅  已更新[更新人]");

            } catch (Exception e) {
                log.error("❌【公共字段填充】更新失败：{}", e.getMessage());
                e.printStackTrace();
            }
        }

        // 5. 填充完成
        log.info("🚀【公共字段填充】字段填充完成！");
    }
}